package com.kongmou.springDataJpaAdvanced.dao.impl;

import java.io.Serializable;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import javax.persistence.EntityManager;

import org.hibernate.query.NativeQuery;
import org.hibernate.transform.Transformers;
import org.hibernate.type.BooleanType;
import org.hibernate.type.DateType;
import org.hibernate.type.DoubleType;
import org.hibernate.type.IntegerType;
import org.hibernate.type.StringType;
import org.hibernate.type.Type;
import org.springframework.data.jpa.repository.support.SimpleJpaRepository;

import com.kongmou.springDataJpaAdvanced.dao.BaseDaoRepository;

/**
 * 实现扩展 SpringBoot第四讲扩展和封装Spring Data JPA(一)_自定义Repository和创建自己的BaseRepository
 * https://www.jianshu.com/p/73f48095a7bf
 *
 * @param <T>
 * @param <ID>
 * @author kongmou
 */
public class BaseRepositoryImpl<T, ID extends Serializable> extends SimpleJpaRepository<T, ID>
        implements BaseDaoRepository<T, ID> {

    private static final String SELECT_PATTERN = "(?i)select(\\s+distinct)?\\s+(.*?)\\s+from";
    private static final String COLUM_PATTERN = "([a-zA-z0-9_]+?)\\s*(,|$)";
    private final EntityManager entityManager;

    public BaseRepositoryImpl(Class<T> domainClass, EntityManager em) {
        super(domainClass, em);
        this.entityManager = em;
    }

    @SuppressWarnings("unchecked")
    @Override
    public List<Object[]> selectBySql(String sql) {
        return entityManager.createNativeQuery(sql).getResultList();

    }

    /**
     * 实现本地带参数查询
     *
     * @throws Exception
     * @throws NoSuchFieldException
     */
    @Override
    public <K> List<K> selectBySql(String sql, Map<String, Object> params, Class<K> clazz) {
        NativeQuery<K> query = (NativeQuery<K>) entityManager.createNativeQuery(sql);
        query.setProperties(params);
        // Session session = entityManager.unwrap(org.hibernate.Session.class);
        // NativeQuery query2 = session.createNativeQuery(sql);
        List<String> columnAliases = analyseSqlColum(sql);
        try {
            for (String columnAlias : columnAliases) {
                Field field = clazz.getDeclaredField(columnAlias);
                Type type = null;
                Class<?> clazz2 = field.getType();
                if (clazz2 == String.class) {
                    type = StringType.INSTANCE;
                } else if (clazz2 == Integer.class || clazz2 == int.class) {
                    type = IntegerType.INSTANCE;
                } else if (clazz2 == Date.class) {
                    type = DateType.INSTANCE;
                } else if (clazz2 == Double.class) {
                    type = DoubleType.INSTANCE;
                } else if (clazz2 == Boolean.class || clazz2 == boolean.class) {
                    type = BooleanType.INSTANCE;
                }

                query.addScalar(columnAlias, type);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        query.setResultTransformer(Transformers.aliasToBean(clazz));
        // query.unwrap(SQLQuery.class).
        // setResultTransformer(Transformers.aliasToBean(clazz));
        List<K> list = query.list();
        return list;
    }

    /**
     * 将sql语句中的查询列解析为一个<b>&nbsp;大写&nbsp;</b>string集合.<br>
     * 列与列之间通过逗号分隔，如使用sql函数中存在逗号，则此方法将产生无效的结果.<br>
     * 可使用 {@link #selectBySql(String, Map, SqlBeanMapper)}
     *
     * @param sql
     * @return
     * @author kongmou
     * @date 2018年4月23日16:36:29
     * @version 1.0
     * @since jdk 1.8
     */
    private List<String> analyseSqlColum(String sql) {
        List<String> colums = new ArrayList<String>();
        Pattern pattern = Pattern.compile(SELECT_PATTERN);
        Matcher matcher = pattern.matcher(sql.replaceAll("[\\n\\t]+", ""));
        if (matcher.find()) {
            String fieldStrs = matcher.group(2);
            pattern = Pattern.compile(COLUM_PATTERN, Pattern.CASE_INSENSITIVE);
            matcher = pattern.matcher(fieldStrs);
            while (matcher.find()) {
                colums.add(matcher.group(1));
            }
        }
        return colums;
    }
}
